package com.icarsaas.saas.tenant.config;

import com.icarsaas.saas.component.comment.bean.CommentIntegrator;
import com.icarsaas.saas.master.model.MasterTenant;
import com.icarsaas.saas.master.repository.MasterTenantRepository;
import com.icarsaas.saas.master.service.MasterTenantService;
import com.icarsaas.saas.tenant.config.properties.MyProperties;
import lombok.extern.slf4j.Slf4j;
import org.hibernate.MultiTenancyStrategy;
import org.hibernate.cfg.Environment;
import org.hibernate.context.spi.CurrentTenantIdentifierResolver;
import org.hibernate.engine.jdbc.connections.spi.MultiTenantConnectionProvider;
import org.hibernate.jpa.boot.spi.IntegratorProvider;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.annotation.Qualifier;
import org.springframework.boot.autoconfigure.condition.ConditionalOnBean;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.ComponentScan;
import org.springframework.context.annotation.Configuration;
import org.springframework.context.annotation.FilterType;
import org.springframework.data.jpa.repository.config.EnableJpaRepositories;
import org.springframework.orm.jpa.JpaTransactionManager;
import org.springframework.orm.jpa.JpaVendorAdapter;
import org.springframework.orm.jpa.LocalContainerEntityManagerFactoryBean;
import org.springframework.orm.jpa.support.OpenEntityManagerInViewFilter;
import org.springframework.orm.jpa.vendor.HibernateJpaVendorAdapter;
import org.springframework.transaction.PlatformTransactionManager;
import org.springframework.transaction.annotation.EnableTransactionManagement;

import java.util.Collections;
import java.util.HashMap;
import java.util.Map;

/**
 * 动态配置多租户数据源.
 * 
 * @author chlingm
 *@ComponentScan(basePackages = { 
		"com.icarsaas.saas.tenant.system.repository",
        "com.icarsaas.saas.tenant.system.model", 
        "com.icarsaas.saas.tenant.test.repository",
        "com.icarsaas.saas.tenant.test.model" 
        })
@EnableJpaRepositories(basePackages = {
        "com.icarsaas.saas.tenant.system.repository",
        "com.icarsaas.saas.tenant.system.service",
        "com.icarsaas.saas.tenant.test.repository",
        "com.icarsaas.saas.tenant.test.service" 
        }
 */
@Configuration @Slf4j
@EnableTransactionManagement
@ComponentScan(basePackages = { 
		"com.icarsaas.saas.tenant.**.model"
        ,"com.icarsaas.saas.tenant.**.repository"
        })
@EnableJpaRepositories(basePackages = {
        "com.icarsaas.saas.tenant.**.repository"
        ,"com.icarsaas.saas.tenant.**.service"
        ,"com.icarsaas.saas.component.base"
        }, 
        entityManagerFactoryRef = "tenantEntityManagerFactory", 
        transactionManagerRef = "tenantTransactionManager")
public class TenantDatabaseConfig {//implements TransactionManagementConfigurer
	
	@Autowired
    MyProperties jpaProperties;
	
    @Bean(name = "tenantJpaVendorAdapter")
    public JpaVendorAdapter jpaVendorAdapter() {
        return new HibernateJpaVendorAdapter();
    }
    
    /**
     * 事务无法生效，repository.save无法生效.
     */
    /*@Bean(name = "tenantTransactionManager")
    public PlatformTransactionManager  transactionManager(@Qualifier("tenantEntityManagerFactory") 
            EntityManagerFactory tenantEntityManager) {
        JpaTransactionManager transactionManager = new JpaTransactionManager();
        transactionManager.setEntityManagerFactory(tenantEntityManager);
        return transactionManager;
    }*/
    /**
     * 使用factory重新new出来一个 JpaTransactionManager
     * @param tenantEntityManagerFactory
     * @return
     */
    @Bean(name = "tenantTransactionManager")
    public PlatformTransactionManager transactionManager(@Qualifier("tenantEntityManagerFactory") 
    LocalContainerEntityManagerFactoryBean tenantEntityManagerFactory) {
    	return new JpaTransactionManager(tenantEntityManagerFactory.getObject());
    }
    
    /**
     * 解决实体懒加载，no session问题
     * application.yml配置spring.jpa.open-in-view=true
     * @return
     */
    @Bean
	public OpenEntityManagerInViewFilter openEntityManagerInViewFilter(){
    	OpenEntityManagerInViewFilter filter = new OpenEntityManagerInViewFilter();
    	filter.setEntityManagerFactoryBeanName("tenantEntityManagerFactory");
		return filter;
	}
    
    @Bean(name = "datasourceBasedMultitenantConnectionProvider")
    @ConditionalOnBean(name = "masterEntityManagerFactory")
    public MultiTenantConnectionProvider multiTenantConnectionProvider() {
        // Autowires the multi connection provider
        return new DataSourceBasedMultiTenantConnectionProviderImpl();
    }

    @Bean(name = "currentTenantIdentifierResolver")
    public CurrentTenantIdentifierResolver currentTenantIdentifierResolver() {
        return new CurrentTenantIdentifierResolverImpl();
    }

    
    @Bean(name = "tenantEntityManagerFactory")
    @ConditionalOnBean(name = "datasourceBasedMultitenantConnectionProvider")
    public LocalContainerEntityManagerFactoryBean entityManagerFactory(
            @Qualifier("datasourceBasedMultitenantConnectionProvider") 
            MultiTenantConnectionProvider connectionProvider,
            @Qualifier("currentTenantIdentifierResolver") 
            CurrentTenantIdentifierResolver tenantResolver) {
    	
        LocalContainerEntityManagerFactoryBean emfBean = new LocalContainerEntityManagerFactoryBean();
        //All tenant related entities, repositories and service classes must be scanned
        /*
        emfBean.setPackagesToScan(
                new String[] { 
                		User.class.getPackage().getName(),
                        UserRepository.class.getPackage().getName(),
                        UserService.class.getPackage().getName() 
                        });
                        */
        emfBean.setPackagesToScan(
                new String[] { 
                		"com.icarsaas.saas.tenant.**.model",
                        "com.icarsaas.saas.tenant.**.repository",
                        "com.icarsaas.saas.tenant.**.service"
                        ,"com.icarsaas.saas.component.base"
                        });
        emfBean.setJpaVendorAdapter(jpaVendorAdapter());
        emfBean.setPersistenceUnitName("tenantdb-persistence-unit");
        Map<String, Object> properties = new HashMap<>();
        properties.put(Environment.MULTI_TENANT, MultiTenancyStrategy.SCHEMA);
        properties.put(Environment.MULTI_TENANT_CONNECTION_PROVIDER, connectionProvider);
        properties.put(Environment.MULTI_TENANT_IDENTIFIER_RESOLVER, tenantResolver);
        // ImprovedNamingStrategy is deprecated and unsupported in Hibernate 5 命名策略
        // properties.put("hibernate.ejb.naming_strategy", "ImprovedNamingStrategy");
        
        String implicitStrategy = jpaProperties.getImplicitStrategy();
        String physicalStrategy = jpaProperties.getPhysicalStrategy();
        properties.put(Environment.IMPLICIT_NAMING_STRATEGY, implicitStrategy);
        properties.put(Environment.PHYSICAL_NAMING_STRATEGY, physicalStrategy);
        
        String dialect = jpaProperties.getDialect();
        boolean showSql = jpaProperties.isShowSql();//是否显示sql
        String ddlAuto = jpaProperties.getDdlAuto();//是否创建ddl
        boolean formatSql = jpaProperties.isFormatSql();
        properties.put(Environment.DIALECT, dialect);
        properties.put(Environment.SHOW_SQL, showSql);
        properties.put(Environment.FORMAT_SQL, formatSql);
        properties.put(Environment.HBM2DDL_AUTO, ddlAuto);
        // 将swagger注解转化成jpa comment
        properties.put("hibernate.use_sql_comments", true);
        properties.put("hibernate.integrator_provider",
                (IntegratorProvider) () -> Collections.singletonList(CommentIntegrator.INSTANCE));

        emfBean.setJpaPropertyMap(properties);
        log.info("tenantEntityManagerFactory set up successfully!");
        return emfBean;
    }
    
}